﻿using System;
using System.Threading;
using Common.Logging;

namespace Samurai.Wakizashi.Workflow
{
    /// <summary>
    /// Workflow step that will suspend the current workflow for the specified amount of time. After 
    /// the specified amount of time has expired, the workflow step marks itself as complete so the 
    /// encompasing workflow can continue execution.
    /// </summary>
    /// <remarks>Note that the workflow suspension does not pause the currently executing thread. Rather, it allows the executing thread to continue and raise the completed event on a seperate thread. For this reason, it is recommended that the next step be performed on a specific thread and not Thread.Current.</remarks>
    /// <typeparam name="T"></typeparam>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "The disposable timer used in this class is disposed of upon timer firing. No need to implement IDisposable.")]
    public class SuspendWorkflowStep<T> : WorkflowStepBase<T> where T : class, IWorkflowContext
    {
        private readonly ILog _log = LogManager.GetLogger(typeof(SuspendWorkflowStep<T>));
        private readonly TimeSpan _lengthOfTimeToPause;
        private Timer _pauseTimer;

        /// <summary>
        /// Initializes a new instance of the <see cref="SuspendWorkflowStep{T}"/> class.
        /// </summary>
        public SuspendWorkflowStep(TimeSpan lengthOfTimeToPause)
        {
            if(lengthOfTimeToPause == TimeSpan.FromSeconds(0)) throw new InvalidOperationException("Length of time to pause cannot be zero seconds");

            _lengthOfTimeToPause = lengthOfTimeToPause;
        }

        #region Overrides of WorkflowStepBase<T>

        /// <summary>
        /// Executes the step.
        /// </summary>
        public override void ExecuteStep()
        {
            _pauseTimer = new Timer(TimeElapsed);
            _pauseTimer.Change(_lengthOfTimeToPause, TimeSpan.FromMilliseconds(-1));

            if (_log.IsDebugEnabled) _log.DebugFormat("Created suspend timer with interval of {0}.", _lengthOfTimeToPause);
        }

        #endregion

        private void TimeElapsed(object state)
        {
            _pauseTimer.Dispose();
            _pauseTimer = null;

            _log.Debug(l => l("Suspend interval elapsed"));

            NotifyStepCompleted();
        }
    }
}
